package com.fxf.srb.core.service.impl;

import com.alibaba.excel.EasyExcelFactory;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fxf.srb.core.ExcelDictDTOListener;
import com.fxf.srb.core.pojo.dto.ExcelDictDTO;
import com.fxf.srb.core.pojo.entity.Dict;
import com.fxf.srb.core.mapper.DictMapper;
import com.fxf.srb.core.service.IDictService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 数据字典 服务实现类
 * </p>
 *
 * @author fxf
 * @since 2021-05-26
 */
@Service
@Slf4j
public class DictServiceImpl extends ServiceImpl<DictMapper, Dict> implements IDictService {

    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 因为这里是每五条数据导入一次数据库，可能在导入数据的过程中出现异常情况，所以这里需要添加事务注解@Transactional，
     * 发生异常时进行回滚，保证数据的一致性
     * @param inputStream
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void importData(InputStream inputStream) {
        // 这里 需要指定读用哪个class去读，然后读取第一个sheet 文件流会自动关闭
        EasyExcelFactory.read(inputStream, ExcelDictDTO.class, new ExcelDictDTOListener(baseMapper)).sheet().doRead();
        log.info("importData finished");
    }

    /***
     * 导出数据字典excel
     * @return
     */
    @Override
    public List<ExcelDictDTO> listDictData() {

        //查询到数据字典数据列表
        List<Dict> dictsList = baseMapper.selectList(null);
        //创建一个List集合并预先指定长度为查询出数据列表的长度，接收对象为ExcelDictDTO
        ArrayList<ExcelDictDTO> excelDictDTOS = new ArrayList<>(dictsList.size());
        //循环遍历数据列表将每项数据copy给excelDictDTO对象并添加到excelDictDTOS集合中
        dictsList.forEach(dictDTO -> {
            ExcelDictDTO excelDictDTO = new ExcelDictDTO();
            BeanUtils.copyProperties(dictDTO, excelDictDTO);
            excelDictDTOS.add(excelDictDTO);
        });
        return excelDictDTOS;

//        return null;
    }


    /**
     * 前端使用懒加载，根据id查询对应的子节点的id
     * @param parentId
     * @return
     */
    @Override
    public List<Dict> listByParentId(Long parentId) {

        List<Dict> dictList = null;
        try {
            //从redis中取值
            log.info("从redis中获取数据");
            dictList = (List<Dict>) redisTemplate.opsForValue().get("srb:core:dictList:" + parentId);
            if (dictList != null){
                return dictList;
            }
        }catch (Exception e){
            log.error("redis服务器异常：" + ExceptionUtils.getStackTrace(e));//此处不抛出异常，继续执行后面的代码
        }

        //从数据库中取值
        log.info("从数据库中获取数据");
        dictList = baseMapper.selectList(new QueryWrapper<Dict>().eq("parent_id", parentId));
        dictList.forEach(dict -> {
            //如果有子节点，则是非叶子节点
            boolean hasChildren = this.hasChildren(dict.getId());
            dict.setHasChildren(hasChildren);
        });

        try {
            //将数据存入到redis中
            log.info("存入数据到redis");
            redisTemplate.opsForValue().set("dictList:" + parentId,dictList,5, TimeUnit.MINUTES);
        }catch (Exception e){
            log.error("redis服务器异常：" + ExceptionUtils.getStackTrace(e));//此处不抛出异常，继续执行后面的代码
        }


        return dictList;
    }

    @Override
    public List<Dict> findByDictCodeList(String dictCode) {
        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        wrapper.eq("dict_code",dictCode);
        Dict dict = baseMapper.selectOne(wrapper);
        return this.listByParentId(dict.getId());
    }

    @Override
    public String getNameByParentDictCodeAndValue(String dictCode, Integer value) {
        QueryWrapper<Dict> wrapper = new QueryWrapper<>();
        wrapper.eq("dict_code",dictCode);
        Dict parentDict = baseMapper.selectOne(wrapper);
        if (parentDict == null){
            return "";
        }

        wrapper = new QueryWrapper<>();
        wrapper.eq("parent_id",parentDict.getId())
                .eq("value",value);
        Dict dict = baseMapper.selectOne(wrapper);
        if (dict == null){
            return "";
        }

        return dict.getName();
    }

    /**
     * 判断该节点是否有子节点
     */
    private boolean hasChildren(Long id) {
        QueryWrapper<Dict> queryWrapper = new QueryWrapper<Dict>().eq("parent_id", id);
        Integer count = baseMapper.selectCount(queryWrapper);
        if(count.intValue() > 0) {
            return true;
        }
        return false;
    }

    /**
     * 查询数据字典列表树形数据
     * @param parentId
     * @return
     */
    @Override
    public List<Dict> listByParentIdTree(Long parentId) {
        List<Dict> res = new ArrayList<>();
        QueryWrapper<Dict> wrapper = new QueryWrapper<Dict>();
        wrapper.eq("parent_id",parentId);
        //根据parentId查询出列表数据
        List<Dict> dicts = baseMapper.selectList(wrapper);
        //遍历查询出来的列表，判断是否包含子节点
        dicts.forEach(dict -> {
            //递归查询子节点数据
            res.add(dict);
            List<Dict> childrenDicts = getChildDictsList(dict.getId());
            if (childrenDicts.size() > 0){
                dict.setChildrenDicts(childrenDicts);
            }

        });
        return res;
    }

    /**
     * 递归查询子节点数据
     * @param parentId
     * @return
     */
    public List<Dict> getChildDictsList(Long parentId){
        //根据节点id查询出数据列表，
        List<Dict> res = new ArrayList<>();
        QueryWrapper<Dict> wrapper = new QueryWrapper<Dict>();
        wrapper.eq("parent_id",parentId);
        List<Dict> dicts = baseMapper.selectList(wrapper);
        dicts.forEach(dict -> {
            //将每一项子节点的数据添加到list中返回添加到children中
            res.add(dict);
            //遍历递归查询出子节点数据
            List<Dict> childrenDicts = getChildDictsList(dict.getId());
            if (childrenDicts.size() > 0){
                dict.setChildrenDicts(childrenDicts);
            }

        });
        return res;
    }
}
